package com.example.app.manager

import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.OrientationHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager

/**
 * Created on 2023/1/29.
 *
 * @author Joker
 * Des:
 */

class RecycleViewExposeManager {

    /**
     * 列表是否逻辑上可见
     */
    private var mRecyclerView: RecyclerView? = null
    private var onItemExpose: ((visible: Boolean, position: Int) -> Unit)? = null
    private var onListExpose: ((firstPos: Int, lastPos: Int) -> Unit)? = null

    /**
     * 设置RecyclerView的item可见状态的监听
     *
     */
    fun setRecyclerItemExposeListener(recyclerView: RecyclerView?, onItemExpose: ((visible: Boolean, position: Int) -> Unit)? = null, onListExpose: ((firstPos: Int, lastPos: Int) -> Unit)? = null) {
        this.onItemExpose = onItemExpose
        this.onListExpose = onListExpose

        mRecyclerView = recyclerView
        if (mRecyclerView == null || mRecyclerView?.visibility != View.VISIBLE) return

        //检测recyclerView的滚动事件
        mRecyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                //SCROLL_STATE_IDLE:停止滚动；
                //SCROLL_STATE_DRAGGING: 用户慢慢拖动
                //SCROLL_STATE_SETTLING：惯性滚动
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    handleCurrentVisibleItems()
                }
            }

            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                //包括刚进入列表时统计当前屏幕可见views
                if (dx == 0 && dy == 0) handleCurrentVisibleItems()
            }
        })
    }

    /**
     * 处理 当前屏幕上mRecyclerView可见的item view
     */
    fun handleCurrentVisibleItems() {
        //View.getGlobalVisibleRect(new Rect())，true表示view视觉可见，无论可见多少。
        if (mRecyclerView == null || mRecyclerView?.visibility != View.VISIBLE || mRecyclerView?.isShown == false || mRecyclerView?.getGlobalVisibleRect(Rect()) == false) {
            return
        }
        //保险起见，为了不让统计影响正常业务，这里做下try-catch
        try {
            var range = IntArray(2)
            var orientation = -1
            val manager = mRecyclerView?.layoutManager
            when (manager) {
                is GridLayoutManager -> {
                    range = findRangeGrid(manager)
                    orientation = manager.orientation
                }

                is LinearLayoutManager -> {
                    range = findRangeLinear(manager)
                    orientation = manager.orientation
                }

                is StaggeredGridLayoutManager -> {
                    range = findRangeStaggeredGrid(manager)
                    orientation = manager.orientation
                }
            }
            if (range.size < 2) return

            if (onListExpose != null) {
                onListExpose?.invoke(range[0], range[1])
            } else {
                // 注意，这里 会处理此刻 滑动过程中 所有可见的view
                for (i in range[0]..range[1]) {
                    val view = manager?.findViewByPosition(i)
                    setCallbackForLogicVisibleView(view, i, orientation)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 为 逻辑上可见的view设置 可见性回调
     * 说明：逻辑上可见--可见且可见高度（宽度）>view高度（宽度）的50%
     *
     * @param view        可见item的view
     * @param position    可见item的position
     * @param orientation recyclerView的方向
     */
    private fun setCallbackForLogicVisibleView(view: View?, position: Int, orientation: Int) {
        if (view == null || view.visibility != View.VISIBLE || !view.isShown || !view.getGlobalVisibleRect(Rect())) {
            return
        }
        val rect = Rect()
        val cover = view.getGlobalVisibleRect(rect)

        //item逻辑上可见：可见且可见高度（宽度）>view高度（宽度）50%才行
        val visibleHeightEnough = orientation == OrientationHelper.VERTICAL && rect.height() > view.measuredHeight / 2
        val visibleWidthEnough = orientation == OrientationHelper.HORIZONTAL && rect.width() > view.measuredWidth / 2
        val isItemViewVisibleInLogic = visibleHeightEnough || visibleWidthEnough
        onItemExpose?.invoke(cover && isItemViewVisibleInLogic, position)
    }

    private fun findRangeLinear(manager: LinearLayoutManager): IntArray {
        val range = IntArray(2)
        range[0] = manager.findFirstVisibleItemPosition()
        range[1] = manager.findLastVisibleItemPosition()
        return range
    }

    private fun findRangeGrid(manager: GridLayoutManager): IntArray {
        val range = IntArray(2)
        range[0] = manager.findFirstVisibleItemPosition()
        range[1] = manager.findLastVisibleItemPosition()
        return range
    }

    private fun findRangeStaggeredGrid(manager: StaggeredGridLayoutManager): IntArray {
        val startPos = IntArray(manager.spanCount)
        val endPos = IntArray(manager.spanCount)
        manager.findFirstVisibleItemPositions(startPos)
        manager.findLastVisibleItemPositions(endPos)
        return findRange(startPos, endPos)
    }

    private fun findRange(startPos: IntArray, endPos: IntArray): IntArray {
        var start = startPos[0]
        var end = endPos[0]
        for (i in 1 until startPos.size) {
            if (start > startPos[i]) {
                start = startPos[i]
            }
        }
        for (i in 1 until endPos.size) {
            if (end < endPos[i]) {
                end = endPos[i]
            }
        }
        return intArrayOf(start, end)
    }
}